2D Game Development
2D
games take place in a grid-like area having only width and height. If
you look at your computer monitor, you’ll see a perfect representation
of a 2D area. Objects in your game can be placed anywhere in this
two-dimensional world, but they remain on the same plane. Figure 1 shows how a 2D world is laid out.
2D game development hasn’t changed much since the first video games. There are still a few key pieces needed for every 2D game:
Textures
Textures are a core
piece of any 2D game. Textures are simply the graphics your game uses.
For instance, any graphics for a character, weapon, or house would use a
texture. Textures are created through the use of applications like
Photoshop, Paint Shop Pro, or even Windows paint. Figure 2 shows examples of textures.
The
graphics you use in your game don’t have to be elaborate; they can even
just be programmer art. Any artwork you do can easily be replaced later
by just swapping out the textures.
Loading a Texture
Textures, like other data
your game needs, will usually be loaded at runtime. Since textures are
an integral part of Direct3D, a few built-in functions are available to
you for handling textures. The function D3DX10CreateTextureFromFile
is used to load in textures from disk. This function supports a variety
of popular graphics formats, such as .BMP, .PNG, and .DDS.
The D3DX10CreateTextureFromFile function takes six parameters.
The first parameter is a pointer to the active ID3D10Device; this was created when Direct3D was initialized.
The second and most key parameter is the filename of the texture to load. This is the full path and filename to the texture.
The third parameter contains a D3DX10_IMAGE_LOAD_INFO structure. This structure is used by the D3DX10CreateTextureFromFile
function to determine how to load the texture. The area of memory to
load the texture and the texture dimensions are just some of the
information contained in this structure. Passing NULL to this parameter allows Direct3D to determine this behavior on its own.
Parameter four is a
pointer to a thread pump interface. The thread pump interface is used to
asynchronously load resources in a background thread. Passing NULL to this parameter will cause the resource to block until the texture is loaded.
The fifth parameter is the ID3D10Resource interface, which will receive the loaded texture. The ID3D10Resource object is used to manage image data.
The final parameter is a pointer to an HRESULT object. If you use the HRESULT passed back from the D3DX10CreateTextureFromFile, you can pass NULL as this parameter.
The following code snippet demonstrates how to use the D3DX10CreateTextureFromFile function.
ID3D10Resource* pD3D10Resource = NULL;
// Loads the texture into a temporary ID3D10Resource object
HRESULT hr = D3DX10CreateTextureFromFile(pD3DDevice,
"C:\\test.bmp",
NULL,
NULL,
&pD3D10Resource,
NULL);
// Make sure the texture was loaded successfully
if (FAILED(hr))
{
return NULL;
}
NULL values were passed into the D3DX10_IMAGE_LOAD_INFO and thread pump parameters.
Before the texture can be used, it needs to be converted to use the appropriate texture resource interface.
Note
Texture sizes should be a power of two for the best support across video cards.
Texture Interfaces
Texture interfaces are
used to manage image data of a certain type. Within Direct3D there are
three main types of texture interfaces:
ID3D10Texture1D— Handles a 1D or image strip type of texture.
ID3D10Texture2D— 2D image data. This is the most common type of texture resource.
ID3D10Texture3D— Image data used to represent volume textures.
Each of these
texture resources contains one or more subresources. The sub-resources
represent the different mip levels of the texture. Mip levels
are decreasingly lower resolution versions of the same texture. Mip
levels allow the system to swap in the proper texture resolution based
on an object’s distance. Objects further away need a lower texture
applied to them since they are not close enough to see all the detail
anyway.
Most of the textures you use in your game will be of the 2D variety and will need to be converted to ID3D10Texture2D resources.
Converting a ID3D10Resource to a Texture Resource
Converting between resource types is actually fairly easy using the COM function QueryInterface. The following code shows how to use the QueryInterface function to convert between the two resource types.
// Translates the ID3D10Resource object into a ID3D10Texture2D object
ID3D10Texture2D* texture2D = NULL;
pD3D10Resource->QueryInterface(__uuidof( ID3D10Texture2D ),
(LPVOID*) &texture2D);
pD3D10Resource->Release();
The code to load in and convert a texture to the correct resource type can be contained in a single helper function called GetTexture2DFromFile.
/*******************************************************************
* GetTexture2DFromFile
* Loads a texture from a file into a ID3D10Texture2D object
* Inputs - LPCSTR the path and filename of the texture
* Outputs - pointer to an ID3D10Texture2D object
*******************************************************************/
ID3D10Texture2D* GetTexture2DFromFile(LPCSTR filename)
{
ID3D10Texture2D* texture2D = NULL;
ID3D10Resource* pD3D10Resource = NULL;
// Loads the texture into a temporary ID3D10Resource object
HRESULT hr = D3DX10CreateTextureFromFile(pD3DDevice,
LPCSTR(filename),
NULL,
NULL,
&pD3D10Resource,
NULL);
// Make sure the texture was loaded in successfully
if (FAILED(hr))
{
return NULL;
}
// Translates the ID3D10Resource object into a ID3D10Texture2D object
pD3D10Resource ->QueryInterface(__uuidof( ID3D10Texture2D),
(LPVOID*)&texture2D);
pD3D10Resource ->Release();
// returns the ID3D10Texture2D object
return texture2D;
}
Texture Details
Occasionally
you’ll need to get certain information from a texture such as its
dimensions or pixel format. This information is available using ID3D10Texture2D’s GetDesc function. This function fills in a D3D10_TEXTURE2D_DESC structure with all the details.
// srcTexture must be a valid ID3D10Texture2D object
D3D10_TEXTURE2D_DESC desc;
srcTexture->GetDesc(&desc);
D3D10_TEXTURE2D_DESC
is just one of the texture description structures available and is
specifically for 2D textures. Direct3D also has the structures D3D10_TEXTURE1D_DESC and D3D10_TEXTURE3D_DESC available for 1D and 3D textures respectively. The content of the structure for 2D textures is shown next.
typedef struct D3D10_TEXTURE2D_DESC {
SIZE_T Width;
SIZE_T Height;
UINT MipLevels;
UINT ArraySize;
DXGI_FORMAT Format;
DXGI_SAMPLE_DESC SampleDesc;
D3D10_USAGE Usage;
UINT BindFlags;
UINT CPUAccessFlags;
UINT MiscFlags;
} D3D10_TEXTURE2D_DESC, *LPD3D10_TEXTURE2D_DESC;
The D3D10_TEXTURE2D_DESC structure will give you additional information such as the format of the texture, stored in the Format variable. For more details regarding this structure, see the DirectX SDK documentation.
Viewing a Texture
Direct3D
doesn’t provide a built-in way to view a texture once it’s loaded. An
easy way to view a texture though is to copy it directly to the back
buffer.
The back buffer can just be
considered another texture resource and Direct3D provides functions for
copying data between textures. The function CopyResource
allows the full contents from one resource to be copied into another.
This is useful when you need a background image copied in full.
Direct3D also provides the CopySubresourceRegion function. This function allows for the copying of rectangular image chunks between resources. The CopySubresourceRegion function takes eight parameters.
The first parameter is the
pointer to the destination texture resource. In this instance, this will
be the back buffer. The second parameter is the destination
subresource. You enter the index of the mip level you want the image
data copied to. The third, fourth, and fifth parameters are the location
in the destination resource to copy the image data to. The sixth and
seventh parameters are the source resource and the source subresource
index. The final parameter is a D3D10_BOX structure. This structure gives the boundary of the image data to copy.
The following code sample shows how to use the CopySubresourceRegion function to copy image data from a source texture to the back buffer.
// Get a pointer to the back buffer texture
ID3D10Texture2D *pBackBuffer;
HRESULT hr = pSwapChain->GetBuffer(0, __uuidof(ID3D10Texture2D),
(LPVOID*)&pBackBuffer);
if(hr != S_OK)
{
return;
}
D3D10_BOX sourceRegion;
sourceRegion.left = 0;
sourceRegion.right = 640;
sourceRegion.top = 0;
sourceRegion.bottom = 480;
sourceRegion.front = 0;
sourceRegion.back = 1;
// Copy part of a texture resource to the back buffer texture
// The last parameter is a D3D10_BOX structure which defines the rectangle to copy to the back
// buffer. Passing in 0 will copy the whole buffer.
pD3DDevice->CopySubresourceRegion(pBackBuffer, 0, 0, 0, 0, srcTexture, 0,
&sourceRegion);
To
get access to the back buffer, your code must access the swap chain
that you created when Direct3D was initialized. The back buffer is then
converted to an ID3D10Texture2D object for use by the CopySubresourceRegion function. Figure 3 shows a texture being displayed.
Note
Texture files, such as
DDS and bitmaps can be viewed in the DirectX Texture Tool (dxTex.exe
found in the Utilities\bin directory).
The CopySubresourceRegion function passed in all zeroes for the location variables causing the texture to be placed in the top-left corner of the screen.